/**
 * 
 */
package com.rpg.osric.player;

import com.dalonedrau.pool.PooledStringBuilder;
import com.dalonedrau.pool.StringBuilderPool;
import com.rpg.base.flyweight.IOPCData;
import com.rpg.osric.globals.OsricEquipmentConsts;
import com.rpg.osric.globals.OsricIOConsts;
import com.rpg.osric.globals.OsricPlayerConsts;

/**
 * Completed:<br>
 * - ability scores defined in {@link OsricPlayerConsts}<br>
 * - races defined in {@link OsricPlayerConsts}<br>
 * - professions defined in {@link OsricPlayerConsts}<br><br>
 * 
 * TO DO:<br>
 * - finalize multi-class definitions<br>
 * @author Donald
 */
public final class OsricCharacter extends IOPCData {
	/** the {@link OsricCharacter}'s base saving throw scores. */
	private float[]	baseSavingThrows;
	/** the {@link OsricCharacter}'s chances to force a door. */
	private float[]	forceDoorChances;
	/** the {@link OsricCharacter}'s full saving throw scores. */
	private float[]	fullSavingThrows;
	/** languages learned. */
	private long	learnedLanguages;
	/** the {@link OsricCharacter}'s modifiers to their saving throw scores. */
	private float[]	modSavingThrows;
	/** the levels for a multi-class character. */
	private int[]	multiLevel;
	/** the number of languages learned. */
	private int		numLanguagesLearned;
	/** the {@link OsricCharacter}'s bonus to hit specific races. */
	private float[]	racialAttackBonuses;
	/** languages known as a result of the player's race. */
	private long	racialLanguages;
	/** the {@link OsricCharacter}'s resistances. */
	private float[]	resistances;
	/** the {@link OsricCharacter}'s secondary modifiers. */
	private float[]	secondaryModifiers;
	/** the {@link OsricCharacter}'s weapon bonuses. */
	private float[]	weaponBonuses;
	/**
	 * Adjusts the {@link OsricCharacter}'s chances to force open a specific
	 * type of door.
	 * @param type the type of door
	 * @param val the chance to open out of 100
	 * @throws Exception if the door type was never defined
	 */
	public void adjustForceDoorChance(final int type, final float val)
			throws Exception {
		if (type < 0
				|| type >= forceDoorChances.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - door type ");
			sb.append(type);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		forceDoorChances[type] += val;
	}
	/**
	 * Adjusts the {@link OsricCharacter}'s bonus to hit specific race.
	 * @param race the race
	 * @param val the bonus or penalty applied
	 * @throws Exception if the race was never defined
	 */
	public void adjustRacialAttackBonus(final int race, final float val)
			throws Exception {
		if (race < 0
				|| race >= racialAttackBonuses.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - race ");
			sb.append(race);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		racialAttackBonuses[race] += val;
	}
	/**
	 * Adjusts the {@link OsricCharacter}'s resistance to specific type of
	 * attack.
	 * @param type the type of attack
	 * @param val the resistance value
	 * @throws Exception if the resistance type was never defined
	 */
	public void adjustResistance(final int type, final float val)
			throws Exception {
		if (type < 0
				|| type >= resistances.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - resistance ");
			sb.append(type);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		resistances[type] += val;
	}
	/**
	 * Adjusts the {@link OsricCharacter}'s bonus or penalty to save versus a
	 * specific type of attack.
	 * @param save the type of save
	 * @param val the bonus or penalty applied
	 * @throws Exception if the race was never defined
	 */
	public void adjustSavingThrowModifier(final int save, final float val)
			throws Exception {
		if (save < 0
				|| save >= modSavingThrows.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - save type ");
			sb.append(save);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		modSavingThrows[save] += val;
	}
	/**
	 * Adjusts the {@link OsricCharacter}'s secondary modifier.
	 * @param type the type of secondary modifier
	 * @param val the bonus or penalty value
	 * @throws Exception if the secondary modifier was never defined
	 */
	public void adjustSecondaryModifier(final int type, final float val)
			throws Exception {
		if (type < 0
				|| type >= secondaryModifiers.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - ability ");
			sb.append(type);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		secondaryModifiers[type] += val;
	}
	/**
	 * Adjusts the {@link OsricCharacter}'s bonus when using a specific weapon.
	 * @param type the type of weapon
	 * @param val the bonus or penalty value
	 * @throws Exception if the weapon type was never defined
	 */
	public void adjustWeaponBonus(final int type, final float val)
			throws Exception {
		if (type < 0
				|| type >= weaponBonuses.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - ability ");
			sb.append(type);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		weaponBonuses[type] += val;
	}
	/** Clears all chances to force a door. */
	public void clearForceDoorChances() {
		for (int i = 0; i < forceDoorChances.length; i++) {
			forceDoorChances[i] = 0;
		}
	}
	/** Clears all learned languages. */
	public void clearLearnedLanguages() {
		learnedLanguages = 0;
		numLanguagesLearned = 0;
	}
	/** Clears all bonuses or penalties to hit specific races. */
	public void clearRacialAttackBonuses() {
		for (int i = 0; i < racialAttackBonuses.length; i++) {
			racialAttackBonuses[i] = 0;
		}
	}
	/** Clears all languages known as a result of the player's race. */
	public void clearRacialLanguages() {
		racialLanguages = 0;
	}
	/** Clears all resistances. */
	public void clearResistances() {
		for (int i = 0; i < resistances.length; i++) {
			resistances[i] = 0;
		}
	}
	/** Clears all bonuses or penalties to save versus specific attacks. */
	public void clearSavingThrowModifiers() {
		for (int i = 0; i < modSavingThrows.length; i++) {
			modSavingThrows[i] = 0;
		}
	}
	/** Clears all weapon bonuses. */
	public void clearSecondaryModifiers() {
		for (int i = 0; i < secondaryModifiers.length; i++) {
			secondaryModifiers[i] = 0;
		}
	}
	/** Clears all weapon bonuses. */
	public void clearWeaponBonuses() {
		for (int i = 0; i < weaponBonuses.length; i++) {
			weaponBonuses[i] = 0;
		}
	}
	/**
	 * Gets the {@link OsricCharacter}'s base bonus or penalty to save versus a
	 * specific type of attack.
	 * @param save the type of save
	 * @return int
	 * @throws Exception if the race was never defined
	 */
	public float getBaseSavingThrow(final int save) throws Exception {
		if (save < 0
				|| save >= baseSavingThrows.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - save type ");
			sb.append(save);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return baseSavingThrows[save];
	}
	/**
	 * Gets the {@link OsricCharacter}'s chances to force open a specific type
	 * of door.
	 * @param type the type of door
	 * @return int
	 * @throws Exception if the door type was never defined
	 */
	public float getForceDoorChance(final int type) throws Exception {
		if (type < 0
				|| type >= forceDoorChances.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - door type ");
			sb.append(type);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return forceDoorChances[type];
	}
	/**
	 * Gets the {@link OsricCharacter}'s full bonus or penalty to save versus a
	 * specific type of attack.
	 * @param save the type of save
	 * @return int
	 * @throws Exception if the race was never defined
	 */
	public float getFullSavingThrow(final int save) throws Exception {
		if (save < 0
				|| save >= fullSavingThrows.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - save type ");
			sb.append(save);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return fullSavingThrows[save];
	}
	/**
	 * Gets the profession level for a multi-class character.
	 * @param profession the profession
	 * @return int
	 * @throws Exception if an error occurs
	 */
	public int getLevelForMulti(final int profession) throws Exception {
		int l = 0;
		if (!super.hasIOFlag(OsricIOConsts.IO_MULTI_CLASS)) {
			throw new Exception("Not a multi-class PC!");
		}
		switch (super.getProfession()) {
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_ASSASSIN:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_ASSASSIN) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_THIEF:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_FIGHTER:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_MAGE:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_MAGE) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_RANGER:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_RANGER) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_ASSASSIN:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_ASSASSIN) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_THIEF:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_ILLUSIONIST:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_ILLUSIONIST) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_MAGE:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_MAGE) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_ILLUSIONIST_THIEF:
			if (profession == OsricPlayerConsts.CLASS_ILLUSIONIST) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_MAGE_THIEF:
			if (profession == OsricPlayerConsts.CLASS_MAGE) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				l = multiLevel[1];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_FIGHTER_MAGE:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				l = multiLevel[1];
			} else if (profession == OsricPlayerConsts.CLASS_MAGE) {
				l = multiLevel[2];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_MAGE_THIEF:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				l = multiLevel[0];
			} else if (profession == OsricPlayerConsts.CLASS_MAGE) {
				l = multiLevel[1];
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				l = multiLevel[2];
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		}
		return l;
	}
	/**
	 * Gets the {@link OsricCharacter}'s modifiers to the saving throw versus a
	 * specific type of attack.
	 * @param save the type of save
	 * @return int
	 * @throws Exception if the race was never defined
	 */
	public float getModSavingThrow(final int save) throws Exception {
		if (save < 0
				|| save >= modSavingThrows.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - save type ");
			sb.append(save);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return modSavingThrows[save];
	}
	/**
	 * Gets the {@link OsricCharacter}'s bonus to hit specific race.
	 * @param race the race
	 * @return int
	 * @throws Exception if the race was never defined
	 */
	public float getRacialAttackBonus(final int race) throws Exception {
		if (race < 0
				|| race >= racialAttackBonuses.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - race ");
			sb.append(race);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return racialAttackBonuses[race];
	}
	/**
	 * Gets the {@link OsricCharacter}'s resistance to specific type of attack.
	 * @param type the type of attack
	 * @return int
	 * @throws Exception if the resistance type was never defined
	 */
	public float getResistance(final int type) throws Exception {
		if (type < 0
				|| type >= resistances.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - resistance ");
			sb.append(type);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return resistances[type];
	}
	/**
	 * Gets the {@link OsricCharacter}'s secondary modifier.
	 * @param type the type of secondary modifier
	 * @return int
	 * @throws Exception if the secondary modifier was never defined
	 */
	public float getSecondaryModifier(final int type) throws Exception {
		if (type < 0
				|| type >= secondaryModifiers.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - ability ");
			sb.append(type);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return secondaryModifiers[type];
	}
	/**
	 * Gets the {@link OsricCharacter}'s bonus when using a specific weapon.
	 * @param type the weapon type
	 * @return int
	 * @throws Exception if the weapon type was never defined
	 */
	public float getWeaponBonus(final int type) throws Exception {
		if (type < 0
				|| type >= weaponBonuses.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - ability ");
			sb.append(type);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return weaponBonuses[type];
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void init() {
		super.addIOFlag(OsricIOConsts.IO_PC);
		super.initAbilityScores(OsricPlayerConsts.ABILITY_TOTAL);
		super.initEquippedItems(OsricEquipmentConsts.EQUIPPED_TOTAL);
		weaponBonuses = new float[OsricEquipmentConsts.WEAPON_TYPE_TOTAL];
		secondaryModifiers =
				new float[OsricPlayerConsts.SECONDARY_MODIFIER_TOTAL];
		resistances = new float[OsricPlayerConsts.RESISTANCE_TYPE_TOTAL];
		forceDoorChances = new float[2];
		racialAttackBonuses = new float[OsricPlayerConsts.RACE_TOTAL];
		baseSavingThrows = new float[OsricPlayerConsts.SAVE_VS_TOTAL];
		modSavingThrows = new float[OsricPlayerConsts.SAVE_VS_TOTAL];
		fullSavingThrows = new float[OsricPlayerConsts.SAVE_VS_TOTAL];
	}
	/**
	 * Determines if the {@link OsricCharacter} knows a specific language.
	 * @param language the language
	 * @return true if the {@link OsricCharacter} knows the language; false
	 *         otherwise
	 */
	public boolean knowsLanguage(final long language) {
		return (racialLanguages & language) == language
				|| (learnedLanguages & language) == language;
	}
	/**
	 * Removes a learned language.
	 * @param language the language
	 */
	public void removeLearnedLanguages(final long language) {
		if ((learnedLanguages & language) == language) {
			numLanguagesLearned--;
			learnedLanguages &= ~language;
		}
	}
	/**
	 * Removes a racial language.
	 * @param language the language
	 */
	public void removeRacialLanguages(final long language) {
		racialLanguages &= ~language;
	}
	/**
	 * Sets the {@link OsricCharacter}'s base saving throw versus a specific
	 * type of attack.
	 * @param save the type of save
	 * @param val the bonus or penalty applied
	 * @throws Exception if the race was never defined
	 */
	public void setBaseSavingThrow(final int save, final float val)
			throws Exception {
		if (save < 0
				|| save >= baseSavingThrows.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - save type ");
			sb.append(save);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		baseSavingThrows[save] = val;
	}
	/**
	 * Sets the {@link OsricCharacter}'s full saving throw versus a specific
	 * type of attack.
	 * @param save the type of save
	 * @param val the bonus or penalty applied
	 * @throws Exception if the race was never defined
	 */
	public void setFullSavingThrow(final int save, final float val)
			throws Exception {
		if (save < 0
				|| save >= fullSavingThrows.length) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Error - save type ");
			sb.append(save);
			sb.append(" is outside array bounds.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		fullSavingThrows[save] = val;
	}
	/**
	 * Sets a learned language.
	 * @param language the language
	 */
	public void setLearnedLanguage(final long language) {
		if ((learnedLanguages & language) != language) {
			numLanguagesLearned--;
			learnedLanguages |= language;
		}
	}
	/**
	 * Sets the profession level for a multi-class character.
	 * @param profession the profession
	 * @param val the level
	 * @throws Exception if an error occurs
	 */
	public void setLevelForMulti(final int profession, final int val)
			throws Exception {
		if (!super.hasIOFlag(OsricIOConsts.IO_MULTI_CLASS)) {
			throw new Exception("Not a multi-class PC!");
		}
		switch (super.getProfession()) {
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_ASSASSIN:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_ASSASSIN) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_THIEF:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_FIGHTER:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_MAGE:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_MAGE) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_RANGER:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_RANGER) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_ASSASSIN:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_ASSASSIN) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_THIEF:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_ILLUSIONIST:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_ILLUSIONIST) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_MAGE:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_MAGE) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_ILLUSIONIST_THIEF:
			if (profession == OsricPlayerConsts.CLASS_ILLUSIONIST) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_MAGE_THIEF:
			if (profession == OsricPlayerConsts.CLASS_MAGE) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				multiLevel[1] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_FIGHTER_MAGE:
			if (profession == OsricPlayerConsts.CLASS_CLERIC) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				multiLevel[1] = val;
			} else if (profession == OsricPlayerConsts.CLASS_MAGE) {
				multiLevel[2] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_MAGE_THIEF:
			if (profession == OsricPlayerConsts.CLASS_FIGHTER) {
				multiLevel[0] = val;
			} else if (profession == OsricPlayerConsts.CLASS_MAGE) {
				multiLevel[1] = val;
			} else if (profession == OsricPlayerConsts.CLASS_THIEF) {
				multiLevel[2] = val;
			} else {
				throw new Exception(
						"Attempting to get invalid level rank for multi-class PC!");
			}
			break;
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setProfession(final int val) {
		switch (val) {
		case OsricPlayerConsts.CLASS_ASSASSIN:
		case OsricPlayerConsts.CLASS_THIEF:
		case OsricPlayerConsts.CLASS_CLERIC:
		case OsricPlayerConsts.CLASS_DRUID:
		case OsricPlayerConsts.CLASS_FIGHTER:
		case OsricPlayerConsts.CLASS_ILLUSIONIST:
		case OsricPlayerConsts.CLASS_MAGE:
		case OsricPlayerConsts.CLASS_PALADIN:
		case OsricPlayerConsts.CLASS_RANGER:
			multiLevel = new int[0];
			super.removeIOFlag(OsricIOConsts.IO_MULTI_CLASS);
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_ASSASSIN:
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_THIEF:
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_FIGHTER:
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_MAGE:
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_RANGER:
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_ASSASSIN:
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_THIEF:
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_ILLUSIONIST:
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_MAGE:
		case OsricPlayerConsts.CLASS_MULTI_ILLUSIONIST_THIEF:
		case OsricPlayerConsts.CLASS_MULTI_MAGE_THIEF:
			multiLevel = new int[] { 1, 1 };
			super.addIOFlag(OsricIOConsts.IO_MULTI_CLASS);
			break;
		case OsricPlayerConsts.CLASS_MULTI_CLERIC_FIGHTER_MAGE:
		case OsricPlayerConsts.CLASS_MULTI_FIGHTER_MAGE_THIEF:
			multiLevel = new int[] { 1, 1, 1 };
			super.addIOFlag(OsricIOConsts.IO_MULTI_CLASS);
			break;
		}
		super.setProfession(val);
		super.setLevel(1);
	}
	/**
	 * Sets a known racial language.
	 * @param language the language
	 */
	public void setRacialLanguage(final long language) {
		racialLanguages |= language;
	}
}
